package cn.com.utils;

import cn.com.bean.ResultResponse;
import cn.com.enums.LanguageEnum;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 1.机器翻译2.0,请填写在讯飞开放平台-控制台-对应能力页面获取的APPID、APISecret、APIKey。
 * 2.目前仅支持中文与其他语种的互译，不包含中文的两个语种之间不能直接翻译。
 * 3.翻译文本不能超过5000个字符，即汉语不超过15000个字节，英文不超过5000个字节。
 * 4.此接口调用返回时长上有优化、通过个性化术语资源使用可以做到词语个性化翻译、后面会支持更多的翻译语种。
 * 注册地址：https://www.xfyun.cn/services/xftrans
 *
 * @author LiuGuodong
 * @date 2024/04/12
 */
@Slf4j
public class MachineTranslationUtil {

    // 请求地址
    private static final String REQUEST_URL = "https://ntrans.xfyun.cn/v2/ots";
    //TODO 控制台获取以下信息
    private static final String APPID = "159f3403";
    private static final String APISecret = "NWZhZGMzNmY0MjQxMGY1OTlkNDBiMmMy";
    private static final String APIKey = "ec0926a0cd498f09feac157145db22d4";


    private static final Map<String, String> cache = new ConcurrentHashMap<>();

    private static final Object lock = new Object();

    public static final String LANGUAGE_TXT = "src/main/resources/language.txt";

    static {
        HashMap<String, String> hashMap = Optional.ofNullable(FileUtil.getReadAllLines(LANGUAGE_TXT)).orElse(new ArrayList<>()).stream()
                .map(line -> line.split("=")).collect(HashMap::new, (map, arr) -> map.put(arr[0], arr[1]), HashMap::putAll);
        cache.putAll(hashMap);

        //开启一个线程，数据量大时，可以考虑使用异步更新缓存
        synchronizationData();
    }

    /**
     * 同步数据
     */
    private synchronized static void synchronizationData() {
        new Thread(() -> {
            while (true) {
                synchronized (lock) {
                    try {
                        if (cache.size() % 100 == 0) {
                            FileUtil.writeToFile(cache, "src/main/resources/language.txt");
                        }
                    } catch (Exception e) {
                        log.error("更新缓存失败：{}", e.getMessage());
                    }
                }
            }
        }).start();
    }

    // 测试主函数
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        try {
            translate("你好，世界！");
            String text = "自然语言处理（Natural Language Processing, NLP）是一门研究计算机科学与人工智能领域的分子科技。";
            translate(text);
            toChinese(text);
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("本次调用耗时: " + (endTime - startTime) + " ms");
    }

    /**
     * 翻译
     *
     * @param text 文本
     * @return {@link String}
     */
    public static String translate(String text) {
        String result;

        if (containsChinese(text) && chinesePercentage(text) > 0.6) {
            result = toEnglish(text);
        } else {
            result = toChinese(text);
        }
        return result;
    }

    public static boolean itsChinese(String text) {
        return containsChinese(text) && chinesePercentage(text) > 0.6;
    }

    /**
     * 判断字符串是否包含中文字符
     *
     * @param str str
     * @return boolean
     */
    public static boolean containsChinese(String str) {
        for (char c : str.toCharArray()) {
            if (isChinese(c)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 计算字符串中中文字符的比例
     *
     * @param str str
     * @return double
     */
    public static double chinesePercentage(String str) {
        int chineseCount = 0;
        for (char c : str.toCharArray()) {
            if (isChinese(c)) {
                chineseCount++;
            }
        }
        return (double) chineseCount / str.length();
    }

    // 判断字符是否为中文字符
    //public static boolean isChinese(char c) {
    //    return c >= 0x4E00 && c <= 0x9FA5;
    //}
    private static boolean isChinese(char ch) {
        // 检查字符是否在中文字符范围内
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(ch);
        return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION;
    }

    /**
     * 到中文
     *
     * @param text 文本
     * @return {@link String}
     */
    public static String toChinese(String text) {
        return translate(text, LanguageEnum.ENGLISH, LanguageEnum.CHINESE);
    }

    /**
     * 到英语
     *
     * @param text 文本
     * @return {@link String}
     */
    public static String toEnglish(String text) {
        return translate(text, LanguageEnum.CHINESE, LanguageEnum.ENGLISH);
    }

    /**
     * 翻译
     *
     * @param text 文本
     * @param from 本次翻译的源语言
     * @param to   目标语言
     * @return {@link String}
     * @throws Exception 例外
     */
    public static String translate(String text, LanguageEnum from, LanguageEnum to) {
        if (StrUtil.isBlank(text)) {
            return null;
        }
        String result = null;
        result = cache.getOrDefault(text, null);
        if (result != null) {
            return result;
        }
        ResultResponse resultMap = null;
        try {
            String body = buildHttpBody(text, from, to);
            Map<String, String> header = buildHttpHeader(body);
            resultMap = HttpClientUtil.post(REQUEST_URL, header, body);
        } catch (Exception e) {
            log.error("翻译失败：{}", e.getMessage());
        }
        JSONObject entries = new JSONObject(resultMap.getResult());
        if (entries == null || entries.getStr("code") != "200") {
            log.error("翻译失败：{}", resultMap.getResult());
            return "";
        }
        JSONObject transResultObject = entries.getJSONObject("data").getJSONObject("result").getJSONObject("trans_result");
        // 获取 dst 字段的值
        result = transResultObject.getStr("dst");
        log.info("翻译前: {} ==> 翻译后:{}", text, result);
        cache.put(text, result);
        return result;
    }

    private static final String TRANS_API_HOST = "https://fanyi-api.baidu.com/api/trans/vip/translate";

    private static final String baidu_appid = "20221110001446079";
    private static final String baidu_securityKey = "rQ7O1bEUJ6oVnuPdInh_";

    /**
     * 由baidu转英语
     *
     * @param query 查询
     * @return {@link String }
     */
    public static String toEnglishByBaidu(String query) {
        return translateByBaidu(query, LanguageEnum.AUTO, LanguageEnum.EN);
    }

    /**
     * 由baidu转中文
     *
     * @param query 查询
     * @return {@link String }
     */
    public static String toChineseByBaidu(String query) {
        return translateByBaidu(query, LanguageEnum.AUTO, LanguageEnum.ZH);
    }

    /**
     * 由baidu翻译
     *
     * @param query 查询
     * @param from  从
     * @param to    到
     * @return {@link String }
     */
    public static String translateByBaidu(String query, LanguageEnum from, LanguageEnum to) {
        String orDefault = cache.getOrDefault(getKey(query, from, to), null);
        if (orDefault != null) {
            return orDefault;
        }
        Map<String, String> params = buildParams(query, from, to);
        String result = getResult(HttpGet.get(TRANS_API_HOST, params));
        cache.put(getKey(query, from, to), result);
        return result;
    }

    /**
     * 获得密钥
     *
     * @param query 查询
     * @param from  从
     * @param to    到
     * @return {@link String }
     */
    private static String getKey(String query, LanguageEnum from, LanguageEnum to) {
        return query + "-" + from.getCode() + to.getCode();
    }

    /**
     * 取result
     *
     * @param jsonStr 杰森海峡
     * @return {@link String }
     */
    private static String getResult(String jsonStr) {
        try {
            // 解析 JSON
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode rootNode = objectMapper.readTree(jsonStr);

            // 获取 trans_result 数组中的第一个对象的 src 字段
            JsonNode transResult = rootNode.path("trans_result").get(0);
            jsonStr = transResult.path("dst").asText();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsonStr;
    }

    /**
     * 构建参数
     *
     * @param query 查询
     * @param from  从
     * @param to    到
     * @return {@link Map }<{@link String }, {@link String }>
     */
    private static Map<String, String> buildParams(String query, LanguageEnum from, LanguageEnum to) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("q", query);
        params.put("from", from.getCode());
        params.put("to", to.getCode());

        params.put("appid", baidu_appid);

        // 随机数
        String salt = String.valueOf(System.currentTimeMillis());
        params.put("salt", salt);

        // 签名
        String src = baidu_appid + query + salt + baidu_securityKey; // 加密前的原文
        params.put("sign", MD5.md5(src));
        return params;
    }


    /**
     * 组装http请求头
     */
    public static Map<String, String> buildHttpHeader(String body) throws Exception {
        Map<String, String> header = new HashMap<String, String>();
        URL url = new URL(REQUEST_URL);

        //时间戳
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date dateD = new Date();
        String date = format.format(dateD);
        //System.out.println("【OTS WebAPI date】\n" + date);

        //对body进行sha256签名,生成digest头部，POST请求必须对body验证
        String digestBase64 = "SHA-256=" + signBody(body);
        //System.out.println("【OTS WebAPI digestBase64】\n" + digestBase64);

        //hmacsha256加密原始字符串
        StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").//
                append("date: ").append(date).append("\n").//
                append("POST ").append(url.getPath()).append(" HTTP/1.1").append("\n").//
                append("digest: ").append(digestBase64);
        //System.out.println("【OTS WebAPI builder】\n" + builder);
        String sha = hmacsign(builder.toString(), APISecret);
        //System.out.println("【OTS WebAPI sha】\n" + sha);

        //组装authorization
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", APIKey, "hmac-sha256", "host date request-line digest", sha);
        //System.out.println("【OTS WebAPI authorization】\n" + authorization);

        header.put("Authorization", authorization);
        header.put("Content-Type", "application/json");
        header.put("Accept", "application/json,version=1.0");
        header.put("Host", url.getHost());
        header.put("Date", date);
        header.put("Digest", digestBase64);
        //System.out.println("【OTS WebAPI header】\n" + header);
        return header;
    }


    /**
     * 构建http主体
     *
     * @param text 文本
     * @param from 从
     * @param to   到
     * @return {@link String}
     * @throws Exception 例外
     */
    public static String buildHttpBody(String text, LanguageEnum from, LanguageEnum to) throws Exception {
        JSONObject body = new JSONObject();
        JSONObject business = new JSONObject();
        JSONObject common = new JSONObject();
        JSONObject data = new JSONObject();
        //填充common
        common.put("app_id", APPID);
        //填充business
        business.put("from", from.getCode());
        business.put("to", to.getCode());
        //填充data
        //System.out.println("【OTS WebAPI TEXT字个数：】\n" + TEXT.length());
        byte[] textByte = text.getBytes("UTF-8");
        String textBase64 = new String(Base64.getEncoder().encodeToString(textByte));
        //System.out.println("【OTS WebAPI textBase64编码后长度：】\n" + textBase64.length());
        data.put("text", textBase64);
        //填充body
        body.put("common", common);
        body.put("business", business);
        body.put("data", data);
        return body.toString();
    }


    /**
     * 对body进行SHA-256加密
     */
    private static String signBody(String body) throws Exception {
        MessageDigest messageDigest;
        String encodestr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(body.getBytes("UTF-8"));
            encodestr = Base64.getEncoder().encodeToString(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return encodestr;
    }

    /**
     * hmacsha256加密
     */
    private static String hmacsign(String signature, String apiSecret) throws Exception {
        Charset charset = Charset.forName("UTF-8");
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(signature.getBytes(charset));
        return Base64.getEncoder().encodeToString(hexDigits);
    }

}
